<!DOCTYPE html>
<html>
<head>
  <title>AWS SDK for JavaScript - Console</title>
  <link rel="stylesheet" href="css/smoothness/jquery-ui-1.10.1.custom.css" />
  <style type="text/css">
    body { font-family: Helvetica, Arial, sans-serif; font-size: 9pt; }
    .padding { padding: 12px; }
    #header { background: #eee; font-size: 1.2em; }
    .selectorControl select, .selectorControl textarea { height: 200px; }
    #operationSelector { border: 1px solid #ccc; }
    #operationSelector h3 { padding: 0; margin-top: 0; }
    #serviceSelector { width: 20%; }
    #operationSelector { width: 20%; }
    #operationData { width: 50%; position: relative; top: 7px; font-family: Monaco, fixed; }
    .tableOutput { width: 100%; overflow: scroll; }
    .history-entry { margin-bottom: 12px; }
    .history-entry pre.output { background: #eee; border: 1px solid #ccc; overflow: scroll; height: 300px; }
    .grid { clear: right; }
    .halfwidth { float: left; width: 48%; margin-right: 6px; }
    .halfwidth:last-child { margin-right: 0px; }
    .break { clear: both; }
    table, th, td { border: 1px solid #ccc; border-collapse: collapse; vertical-align: top; }

    #login-buttons { float: right; }

    /* jquery-ui overrides */
    :focus { outline-width: 0; }
  </style>
  <script type="text/javascript" src="js/jquery-1.9.1.js"></script>
  <script type="text/javascript" src="js/jquery-ui-1.10.1.custom.min.js"></script>
  <script type="text/javascript" src="../../../dist/aws-sdk-all.js"></script>
  <script type="text/javascript" src="appinfo.js"></script>
  <script type="text/template" id="template-historyEntry">
    <div id="history-{entry}" class="history-entry">
      <ul>
        <li>
          <a href="#history-tab-{entry}">
            <img src="img/loading.gif"/>
            {service}.{operation}() &mdash; {date}
          </a>
        </li>
        <li><a href="#history-details-tab-{entry}">Details</a></li>
        <li><a href="#history-table-tab-{entry}">Tabular (Experimental)</a></li>
      </ul>
      <div id="history-tab-{entry}">
        <div class="grid">
          <div class="halfwidth">
            <h3>Response</h3>
            <pre class="padding output operationResponse"></pre>
          </div>
          <div class="halfwidth">
            <h3>Request</h3>
            <pre class="padding output operationRequest"></pre>
          </div>
        </div>
        <div class="break"></div>
      </div>
      <div id="history-details-tab-{entry}">
        <div class="grid">
          <div class="halfwidth">
            <h3>Request Details</h3>
            <pre class="padding output operationRequestDetails"></pre>
          </div>
          <div class="halfwidth">
            <h3>Response Details</h3>
            <pre class="padding output operationResponseDetails"></pre>
          </div>
        </div>
        <div class="break"></div>
      </div>
      <div id="history-table-tab-{entry}">
        <div class="tableOutput"></div>
      </div>
    </div>
  </script>
  <script type="text/javascript">
    services = {};
    currentServiceVar = null;
    currentService = null;
    currentOperation = null;
    data = null;
    error = null;
    response = null;
    requestCount = 0;

    function s4() {
      return Math.floor((1 + Math.random()) * 0x10000)
                 .toString(16)
                 .substring(1);
    };

    function guid() {
      return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
             s4() + '-' + s4() + s4() + s4();
    }

    /**
     * Turns a JS object into a table representation using a set of hints
     * to detect the key/value structure.
     *
     * Assume EC2 for now
     */
    function tableView(items, resultKey) {
      var table = $('<table/>').attr('cellpadding', 5);

      // transposed table for structures
      if (!Array.isArray(items)) {
        for (var key in items) {
          var row = $('<tr/>');
          $('<th/>').text(key).appendTo(row);
          $('<td/>').text(items[key]).appendTo(row);
          row.appendTo(table);
        }
        return table;
      }

      if (items.length === 0) return '';
      var i;
      // first build the headers
      var row = $('<tr/>');
      var keys = typeof items[0] === 'object' ? Object.keys(items[0]) : [resultKey];
      for (i = 0; i < keys.length; i++) {
        $('<th/>').text(keys[i]).appendTo(row);
      }
      row.appendTo(table);

      for (i = 0; i < items.length; i++) {
        row = $('<tr/>');
        if (typeof items[i] === 'object') {
          for (var key in items[i]) {
            var value = items[i][key];
            if (typeof value === 'object') { // complex type
              value = tableView(value);
            }
            $('<td/>').html(value).appendTo(row);
          }
        } else {
          $('<td/>').html(items[i]).appendTo(row);
        }
        row.appendTo(table);
      }

      return table;
    }

    function loadOperationsForService(svc) {
      if (currentServiceVar === svc) return;
      currentServiceVar = svc;
      currentService = services[svc];

      $('#operationSelector').empty();
      for (var key in currentService.api.operations) {
        var el = $('<option/>').attr('value', key).text(key);
        $('#operationSelector').append(el).change(function() {
          loadOperation($(this).val());
        });
      }
    }

    function loadOperation(operation) {
      if (currentOperation === operation) return;
      currentOperation = operation;

      var input = currentService.api.operations[operation].input;
      var obj = {shapeMap: {}};
      var sampleInput = inputForShape.call(obj, input);
      sampleInput = JSON.stringify(sampleInput, null, 2);
      sampleInput = sampleInput.replace(/(\n\s*)"(.+?)":/g, '$1$2:');
      $('#operationData').val(sampleInput);
    }

    function inputForShape(shape) {
      switch (shape.type) {
        case 'structure': return inputForStructure.call(this, shape);
        case 'map': return inputForMap.call(this, shape);
        case 'list': return inputForList.call(this, shape);
        case 'string': return "STRING";
        case 'integer': return 123;
        case 'timestamp': return "new Date()";
        default: return 'UNKNOWNVALUE';
      }
    }

    function inputForMap(shape) {
      return {someKey: inputForShape.call(this, shape.value)};
    }

    function inputForList(shape) {
      var value = inputForShape.call(this, shape.member);
      return [value, value, value];
    }

    function inputForStructure(shape) {
      var self = this, obj = {};
      AWS.util.each(shape.members, function(key, member) {
        if (shape.required.indexOf(key) < 0) return;
        obj[key] = inputForShape.call(self, member);
      });
      return obj;
    }

    function loadServices() {
      $('#serviceSelector').empty();
      for (var key in AWS) {
        if (AWS[key].serviceIdentifier) {
          var svcVarName = AWS[key].serviceIdentifier;
          try {
            var svc = services[svcVarName] = window[svcVarName] = new AWS[key]();
            var fullName = svc.api.abbreviation || svc.api.fullName;
            var el = $('<option/>').attr('value', svcVarName).text(fullName);
            $('#serviceSelector').append(el).change(function () {
              loadOperationsForService($(this).val());
            });
          } catch (e) {
            console.log('Failed to load service ' + key + ': ' + e.message);
          }
        }
      }
    }

    function executeOperation() {
      var params = eval('var _=' + ($('#operationData').val() || '{}') + ';_');
      currentService[currentOperation](params).send();
    }

    function template(name, options) {
      var html = $('#template-' + name).html();
      html = html.replace(/\{(.+?)\}/g, function(matches, key) {
        return key in options ? options[key] : '';
      });
      return html;
    }

    function addHistoryEntry(req) {
      req.id = guid();

      // add entry to page
      $('#history :first').tabs('option', 'active', false);
      $('#history').prepend(template('historyEntry', {
        operation: req.operation,
        service: req.service.api.endpointPrefix,
        entry: req.id,
        date: new Date(),
      }));

      var reqHttpData = JSON.stringify(req.httpRequest, null, 2);
      var entry = $('#history-' + req.id);
      entry.tabs({collapsible: true});
      entry.find('.operationRequestDetails').text(reqHttpData);
    }

    function updateHistoryEntry(resp) {
      var req = resp.request;
      var entry = $('#history-' + req.id);
      var params = JSON.stringify(req.params);
      var time = new Date().getTime();
      var elapsed = (time - (resp.requestStarted || time)) / 1000;
      var service = req.service.api.endpointPrefix;
      var statusCode = resp.httpResponse.statusCode || 'ERR';
      var retryCount = resp.retryCount || 0;
      var operation = req.operation;
      var info = statusCode + ' ' + elapsed + 's ' + retryCount + ' retries';

      var data;
      if (resp.error) {
        data = resp.error.message + '\n' + resp.error.stack;
      } else {
        data = AWS.util.copy(resp.data);
        if (data.Body) {
          delete data.Body.parent; // remove circular ref
          data.Body = data.Body.toString();
        }
        data = JSON.stringify(data, null, 2);

        var tableData = resp.data;
        var resultKey = '';
        entry.find('.tableOutput').html(tableView(tableData, resultKey));
      }

      // debug to console
      var opInfo = service + '.' + operation + '(' + params + ')';
      console.log('[AWS ' + info + ']', opInfo);

      entry.find('.operationResponse').text(data);
      entry.find('.operationRequest').text(params);
      var link = entry.children('ul').find('a').first();
      link.text('[' + info + '] ' + link.text());

      if (resp.httpResponse.body) {
        delete resp.httpResponse.body.parent; // remove circular reference
        resp.httpResponse.body = resp.httpResponse.body.toString();
      }
      var respHttpData = JSON.stringify(resp.httpResponse, null, 2);
      entry.find('.operationResponseDetails').text(respHttpData);
    }

    function startRequest(resp) {
      addHistoryEntry(resp.request);
      resp.requestStarted = new Date().getTime();
    }

    function endRequest(resp) {
      // set window properties for console debugging
      window.response = resp;
      window.data = resp.data;
      window.error = resp.error;
      window.requestCount++;

      // update history info
      try {
        updateHistoryEntry(resp);
      } catch (err) {
        console.log(err.message);
        console.log(err.stack);
      }
    }

    function loadEvents() {
      AWS.events.
        on('send', startRequest).
        on('complete', endRequest);
    }

    function loaded() {
      $('#selectOperation').tabs({collapsible: true});
      $('#executeOperation').click(executeOperation);
      $('#header .title').text('AWS SDK for JavaScript ' + AWS.VERSION);
    }

    function init() {
      loadEvents();
      loaded();
    }

    function loadIDPCredentials(provider, accessToken) {
      var params = {
        AccountId: window.appInfo.accountId,
        RoleArn: window.appInfo.roleArn,
        IdentityPoolId: window.appInfo.identityPoolId,
        Logins: {}
      };
      params.Logins[provider] = accessToken;

      AWS.config.credentials = new AWS.CognitoIdentityCredentials(params);
      AWS.config.credentials.get(function (err) {
        if (err) {
          console.log(err.message);
          console.log(err.stack);
        }
        else loadServices();
      });
    }

    $(init);
  </script>
</head>
<body>
  <div id="fb-root"></div>
  <script>
    window.fbAsyncInit = function() {
      // init the FB JS SDK
      FB.init({
        appId: window.appInfo.facebook.appId,
        status: false,
        xfbml: false
      });

      FB.Event.subscribe('auth.authResponseChange', function(response) {
        if (response.status === 'connected') {
          loadIDPCredentials('graph.facebook.com',
                             response.authResponse.accessToken);
        }
      });

      FB.getLoginStatus();
      $('#login-facebook').click(function() { FB.login() });
    };

    // Load the SDK asynchronously
    (function(d, s, id){
       var js, fjs = d.getElementsByTagName(s)[0];
       if (d.getElementById(id)) {return;}
       js = d.createElement(s); js.id = id;
       js.src = "//connect.facebook.net/en_US/all.js";
       fjs.parentNode.insertBefore(js, fjs);
     }(document, 'script', 'facebook-jssdk'));
  </script>

  <div id="amazon-root"></div>
  <script type="text/javascript">
    window.onAmazonLoginReady = function() {
      amazon.Login.setClientId(window.appInfo.amazon.appId);

      $('#login-amazon').click(function() {
        amazon.Login.authorize({scope: 'profile'}, function(response) {
          if (!response.error) {
            loadIDPCredentials('www.amazon.com', response.access_token);
          }
        });
      });
    };

    (function(d) {
      var a = d.createElement('script'); a.type = 'text/javascript'; 
      a.async = true; a.id = 'amazon-login-sdk'; 
      a.src = 'https://api-cdn.amazon.com/sdk/login1.js';
      d.getElementById('amazon-root').appendChild(a);
    })(document);
  </script>

  <script type="text/javascript">
    (function() {
     var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
     po.src = 'https://apis.google.com/js/client:plusone.js';
     var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
   })();

   function loginToGoogle(response) {
     if (!response.error) {
       loadIDPCredentials('accounts.google.com', response.id_token);
     }
   }
  </script>

  <div id="header" class="padding">
    <span class="title"></span>
    <div id="login-buttons">
      <button id="login-amazon">Login with Amazon</button>
      <button id="login-facebook">Login with Facebook</button>
      <button id="login-google">
        <span
          class="g-signin"
          data-height="short"
          data-callback="loginToGoogle"
          data-clientid="156058024465.apps.googleusercontent.com"
          data-cookiepolicy="single_host_origin"
          data-requestvisibleactions="http://schemas.google.com/AddActivity"
          data-scope="https://www.googleapis.com/auth/plus.login">
        </span>
      </button>
    </div>
  </div>
  <div id="selectOperation">
    <ul>
      <li><a href="#selectOperationTab">Select an operation</a></li>
    </ul>
    <div id="selectOperationTab">
      <div class="selectorControl">
        <select size="10" id="serviceSelector"></select>
        <select size="10" id="operationSelector"></select>
        <textarea id="operationData"></textarea>
      </div>
      <div class="runControl">
        <button id="executeOperation">Run</button>
      </div>
    </div>
  </div>
  <h2>Requests</h2>
  <div id="history">
  </div>
</body>
</html>
